/**
Author: Maciej Oczko
Date: November 2011
*/

#ifndef POOLMEMORYMANAGERGENERIC_H_
#define POOLMEMORYMANAGERGENERIC_H_

#include <cassert>

template<typename T>
class PoolMemoryManagerGeneric 
{
private:
	/// Array of objects.
	T *m_Data;
	/// Array of pointers to free elements.
	T **m_FreeObjects;
	/// First available object.
	unsigned m_FreeCount;
	/// Number of T type objects in allocated memory.
	unsigned m_Capacity;

	/// Disable copying.
	PoolMemoryManagerGeneric(const PoolMemoryManagerGeneric &);
	PoolMemoryManagerGeneric & operator=(const PoolMemoryManagerGeneric &);

	/// Returns new instance of T type object.
	T * NewInstance()
	{
		assert(m_FreeCount && "No free instances available for allocation.");
		return m_FreeObjects[--m_FreeCount];
	}
public:
	typedef T value_type;

	PoolMemoryManagerGeneric(unsigned p_Capacity) : m_Capacity(p_Capacity)
	{
		assert(m_Capacity > 0 && "Invalid size specified.");
		m_Data = new T[m_Capacity];
		m_FreeObjects = new T*[m_Capacity];
		assert(m_Data && "Not enough memory to allocate object data.");
		assert(m_FreeObjects && "Not enough memory to allocate pointer stack.");
		FreeAll();
	}
	~PoolMemoryManagerGeneric()
	{
		if(m_Data) delete [] m_Data;
		if(m_FreeObjects) delete [] m_FreeObjects;
	}
	/// Makes all instances available for allocation.
	void FreeAll()
	{
		unsigned index = m_Capacity - 1;
		for(m_FreeCount = 0; m_FreeCount < m_Capacity; m_FreeCount++)
		{
			m_FreeObjects[m_FreeCount] = &(m_Data[index--]);
		}
	}
	/// Returns a pointer to new instance of T type. Initialized basic types.
	T * New()
	{
		T * ptr = NewInstance();
		return new (ptr) T();
	}
	/// Returns a pointer to new instance of T type. Uninitialized basic types.
	T * NewU()
	{
		T * ptr = NewInstance();
		return new (ptr) T;
	}
	/// Reclaims the instance referenced by p_Instance.
	void Delete(T *p_Instance)
	{
		assert(BelongsTo((void*)p_Instance) && "An object is not from this memory block.");
		assert((m_FreeCount < m_Capacity) && "You have freed at least one instance more than once." );
		m_FreeObjects[m_FreeCount++] = p_Instance;
	}

	/// Returns used objects count.
	unsigned GetUsedCount() const { return m_Capacity - m_FreeCount; }
	/// Returns free objects count.
	unsigned GetFreeCount() const { return m_FreeCount; }
	/// Returns the capacity of memory block.
	unsigned GetCapacity() const {  return m_Capacity; }
	/// Returns elemt size.
	unsigned GetElementSize() const { return sizeof(T); }

	/// Returns size of used objects count.
	unsigned GetUsedSize() const { return GetUsedCount() * sizeof(T); }
	/// Returns size of free objects count.
	unsigned GetFreeSize() const { return GetFreeCount() * sizeof(T); }
	/// Returns the size of allocated memory block.
	unsigned GetAllSize() const { return m_Capacity * sizeof(T); }

	/// Returns true if memory block is free.
	bool IsEmpty() const { return m_FreeCount == m_Capacity; }
	/// Returns true if memory block is full.
	bool IsFull() const { return m_FreeCount == 0; }
	/// Returns true if given address belongs to this memory block.
	bool BelongsTo(void *ptr) const { return (ptr >= m_Data) && (ptr < m_Data + m_Capacity * sizeof(T) ); }
};

#endif